Introduction

Undertaking a systematic data exploration prior to applying statistical analysis techniques to experimental data can help improve the statistical validity of results and associated conclusons. Key to this is a protocol for ensuring the scientist does not discover a false covariate effect (type I error) or wrongly dismiss a model was valid covariate (type II error).

The aim of this vignette is to provide a protocol for data exploration that identifies potential problems before any statistical analysis is undertaken. The approach outlined makes extensive use of the material discussed in 1. Datasets are sourced from the Internet or academics at Oxford Brookes University.

The data exploration protocol outline is as follows:

  1. Outliers - observations that are relatively large or small when compared to the majority of observations, can significantly affect a statisical result.
  2. Homogeneity of variance - a number of statistical techniques assume homogeneity of variance of data. If this is violated the null hypothesis maybe falsely rejected or the power of the test maybe decrease.
  3. Normality - if data are not normally distributed, a number of statistical tests may not be valid, and alternative approaches will be required
  4. Zero-inflation - when modelling species counts, a large proportion of the data maybe zeros. In such cases alternative statistical models should be used to avoid biased estimates.
  5. Collinearity - avoiding correlation between covariates is important otherwise statistical outcomes may not be directly associated with specific covariates.
  6. Associations - in univariate analysis it is important to explore the indicative relationship between response variables and covariates.

Are there outliers?

In some statistical techniques the results are dominated by outliers; other techniques treat them like any other value. For example, outliers may cause overdispersion in a Poisson GLM. Outliers are defined as an observation that has a relatively large or small value compared to the majority of observations.

In order to demonstrate the data exploration step we will use data from species group sizes in the Naboisho Conservancy within Kenya. Here researchers recorded the size of species groups as they drove along a number of 2km transects.

Boxplots

The boxplot visualizes the median of the data and the spread of the data about it. In the chart below the:

  • Median is presented as a vertical line within the white box
  • 25% and 75% percentiles form a box around the median that contains half of the observations
  • Ends of the thin line either side of the white box are the upper and lower whiskers
  • Upper whisker covers values no larger (or smaller) than the inter-quartile range (the distance between the first and third quartiles)
  • Data beyond the whiskers are the outliers, and are shown as red dots.
naboisho %>% 
  filter(species != "None") %>%
  ggplot(aes(x=species, y=log10(size))) +
  geom_boxplot(outlier.colour="red", 
               outlier.shape=16,
               outlier.size=0.75, 
               notch=F) +
  coord_flip() +
  ggtitle("Species group sizes in Naboisho Conservancy. 2016 to 2019") +
  xlab("Species") +
  ylab("Species group size - log10") +
  theme_bw()

Note the x axis is scaled to log base 10, in order to compare species with small group sizes, such as Black Backed Jackal, against species with large group sizes such as Zebra or Wildebeest.

Cleveland dot plot

Cleveland dotplot is a chart in which the row number of an observation is plotted vs. the observation value, thereby providing a more detailed view of individual observations than a boxplot allows. Points that stick out on the right-hand side, or on the left-hand side, are observed values that are considerable larger, or smaller, than the majority of the observations, and require further investigation.

The chart below shows a dot plot for each species within the Naboisho dataset from 2016 to 2019.

rr naboisho %>% filter(species != ) %>% # Remove species with no name! ggplot(aes(x=size, y=seq(1, length(size)))) + geom_point(alpha=0.3) + ggtitle(dot plot of Naboisho species 2016 to 2019) + labs(x = group size of Giraffe, y = of data) + theme_bw() + facet_wrap(~species, ncol = 2, scales = _x)

For Giraffe we can see that there is a single outlier; a group size of 350 Giraffe. This is quite clearly a data entry error. If we remove this outlier and replot the dot plot, we see the following:

rr naboisho %>% filter(species == ) %>% filter(size <= 300) %>% ggplot(aes(x=size, y=seq(1, length(size)))) + geom_point(alpha=0.3) + ggtitle(dot plot of Giraffe Naboisho 2016 to 2019 - outliers removed) + labs(x = group size of Giraffe, y = of data) + theme_bw()

Note we also have a number of observations with group sizing of zero. This is clearly not possible and is probably due to a misunderstanding on behalf of the data recorder, and so we can correct this mistake accordingly:

rr naboisho <- naboisho %>% mutate_all(funs(replace(., size == 0, 1)))

Implications

Having determined that there are outliers in observations, we can take a number of steps to validate their inclusion or not:

  • check the data to ensure that there has not been a data entry error
  • check the scientific literature to provide an expected value.
  • generate a number of observations randomly from an appropriate distribution, and determine how the proportion of extremem points compares to the field data

If the outliers are deemed valid then it maybe necessary to only use statistical methods that can handle such over-dispersed data. For example, Poisson or negative-binomial distributions for count data.

Homogeneity of variance

Homogeneity of variance is an important assumption in analysis of variance (ANOVA). The plot below shows a boxplot for Wildebesst group size observations within the Naboisho dataset. In the plots below we have assumed that the long rains are from April to May, the short rains are in November and all other months are the dry season.

To apply ANOVA to these data to determine if mean Wildebeest group size varies by year, season or a combination of year and season, we must assume that variation:

  1. In the years are similar
  2. In observations for each season are similar
  3. Between seasons within years are similar.

rr # Add a column to the data set to determine month of year naboisho <- naboisho %>% mutate(Month = month(Date, label= T, abbr = T)) %>% # Add a season column to data mutate(season = case_when( Month ==  ~ rains, #long rains Month ==  ~ rains, #long rains Month ==  ~ rains, #short rains TRUE ~ )) # Else it’s the dry season

wildebeest_seasons <- naboisho %>%
# Take Widebeest only, and remove outliers for group size > 500 filter(species == , size < 500) %>% # Filter out 2016 as insufficient data to determine month filter(year != 2016) wildebeest_seasons %>% # Plot boxplots by season, don’t display outliers ggplot(aes(x=season, y=size)) + geom_boxplot(outlier.shape=NA) + # Facet by year facet_wrap(. ~ year) + coord_cartesian(ylim=c(0, 90)) + theme_bw() + ggtitle(of Wildebeest group size by season ```

We can see that the variance between seasons within a year is significantly different for 2017 and 2019. Therefore we should not undertake the classic ANOVA test if we want to analyse differences in group means within the years 2017 and 2019. Also we have more than two groups and because the size data are counts, we should not assume they are normally distributed.

Levenne’s homogenrity of variance test

We can directly test for homogeneity of variances by using Levenne’s test:

rr library(car) # Generate a two-way test to incorporate interaction between seasons and years leveneTest(size ~ as.factor(season) * as.factor(year), data = wildebeest_seasons)

Levene's Test for Homogeneity of Variance (center = median)
        Df F value    Pr(>F)    
group    7  24.722 < 2.2e-16 ***
      9744                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

From the output above we can see that the p-value is much less than the significance level of 0.05. This means that we can not assume equal variances betweens years and seasons.

We therefore need to use a test that does not assume equal variance, can work for three different groups and is non-parametric. The Kruskal-Wallis test is a nonparametric (distribution free) test, and is used when the assumptions of one-way ANOVA are not met. The test can assess for significant differences on a continuous dependent variable (in our case, group size) by a two or more categorical independent variable groups (in our case the three different seasons). With this test we can ask the question if the mean group size between long rains, short rains and dry seasons is statistically different:

rr # Select 2017 wildebeest observations only wildebeest_seasons %>% filter(year == 2017) %>% kruskal.test(.\(size, .\)season, data = .)


    Kruskal-Wallis rank sum test

data:  .$size and .$season
Kruskal-Wallis chi-squared = 292.62, df = 2, p-value < 2.2e-16

As p << 0.05, we can therefore reject the null hypothesis that the difference in means is zero. So the mean group size does vary by season in 2017. But between which groups is the differnce most significant? To answer this we need a post-hoc test, and for this we use a pairwise wilcox test:

rr wildebeest_seasons %>% filter(year == 2017) %>% pairwise.wilcox.test(.\(size, .\)season, data=.)


    Pairwise comparisons using Wilcoxon rank sum test 

data:  .$size and .$season 

            dry    long rains
long rains  <2e-16 -         
short rains 0.025  <2e-16    

P value adjustment method: holm 

The results show us that the least significant differnce in 2017 was between the dry and short rains seasons, as confirmed by the boxplots above. We can apply the Kruskal-Wallis test across the entire naboisho dataset. Thsi time we’ll test if year on year mean group sizes changes are significant:

rr # Apply Kruskal-Wallis to each species to see if change in mean year on year is significant library(tidyverse) species_kw_test <- naboisho %>% # Filter species recorded as None filter(species != ) %>% # Count testable data count(species, year) %>% # Remove any count less than 10 filter(n>10) %>% # Create testable data set left_join(naboisho) %>% # Nest each species group nest(-species) %>% # Map each group to the Kruskal test mutate(kruskal_res = map(data, ~ kruskal.test(.x\(size, .x\)year)), kruskal = map(kruskal_res, broom::tidy)) %>% # We dont needed the nested data frames anymore select(-data) %>% # Unnest the result unnest(kruskal)

Joining, by = c(\species\, \year\)All elements of `...` must be named.
Did you want `data = c(year, n, X, Date, Distance..m., Compass.Bearing, distance, 
    size, Region.Label, Sample.Label, Area, lat, long, Effort, 
    replicate, Month, season)`?

rr # Select species where we cannot reject the null-hypothesis species_kw_test %>% filter(.$p.value > 0.05) r NA

The table above lists the species where cannot infer anything from year-on-year mean group size changes.

Implications

Statistical modelling using regression will typically assume homogenous variance. This can be checked by plotting the model residuals against the fitted values. A good fit will have have similar residual variation across all values. In order to address heterogeneity of variance in the response variable, it is possible to transform it; for example via a log transformation. Alternatively, modelling approaches that do not assume equal variance can be used.

Normality

A significant number of statistical modelling tools assuming the data are normally distributed. For example, linear regression assumes the data us normally distributed.

Histogram

The following plot shows a histogram for the weight of 1,193 sparrows 2.

# Load sparrows data set
sparrows <- read.delim("/Users/anthony/Documents/GitHub/ComputationalEcology/data_analysis_files/Sparrows.txt")

ggplot(sparrows, aes(x = wt)) + 
  geom_histogram(color = "black", binwidth = 0.25) +
    theme_minimal() +
    labs(x = "Weight (g)", 
         y = "Frequency")

Density plot

Whislt the histograme appears to have an approximate normal shape, we can plot a density plot to have a better visual check?

rr library(ggpubr) ggdensity(data = sparrows$wt, main = plot of sparrow weight, xlab = weight)

Q-Q Plot

The normal Q-Q plot is an alternative graphical method of assessing normality to the histogram. The scatter compares the data to a perfect normal distribution. The scatter should lie as close to the line as possible with no obvious pattern coming away from the line for the data to be considered normally distributed. The Q-Q plot for the sparrow weight data is plotted below:

rr ggqqplot(sparrows$wt)

We can see that the Q-Q plot departs from the theoretical line at extremes. If we subset the data by month and plot a sparrow weight histograme for each month, we can see how the data varies considerably throughout the year.

rr sparrows %>% mutate(Month = lubridate::month(Month, abbr = T, label = T)) %>% ggplot(aes(x = wt)) + geom_histogram(color = , binwidth = 0.25) + theme_minimal() + labs(x = (g), y = ) + facet_wrap(. ~ Month)

This is most likely due to sampling bias; more samples during the summer. If we plot a historgram of the month the observation was recorded we do indeed see a skew towards summer months:

rr sparrows %>% mutate(Month = lubridate::month(Month, abbr = T, label = T)) %>% ggplot(aes(Month, fill=Month)) + geom_histogram(stat=) + theme_bw()

Implications

If tests suggest that the data are not normally distributed it may be possible to log transform (or take the square root) the response variable and then repeat the normality checks. If we need to undertake further tests that are depednent on normality, for example ANOVA, then we can use non parametric tests such as the Kruskal-Wallis test for a one-way ANOVA test.

Zero inflation

When studying species populations ecologists will often record species count data, and then seek to study how species abundance varies as a function of covariates such as time, habitat type, temperature, weather etc. If species are not observed during a survey, this is recorded as an absence. This can lead to over-dispersion within the data.

This being the case we should proceed with modelling techniques that are based on zero-inflated probability distribution. Such distributions allow for frequent zero-valued observations and one common one is a zero-inflated Poisson (ZIP) model, as well as the zero-inflated negative binomial (ZINB).

In order to determine the number of zeros within a dataset, we can simply plot a histogram of occurence data. Often within survye data species absense is implicit in that only a species presence is recorded. If this is the case then the first thing we should do is record the absence explicitly. In this way, we have a true record of the sampling effort.

If we look at the species data for Giraffe from the Naboisho data set:

So we can see that there were 1099 transects undertaken, but only 361 of them have records of Giraffe. So we have not explicitly recorded the fact that during 738 transects no Giraffe were observed. The histogram shows that the vast majority of observations were just for one Giraffe. So let’s update the data to incorporate this:

rr # Number of transects where Giraffe were recorded giraffe_transects <- naboisho %>% filter(species == ) %>% distinct(Sample.Label, Date)

Select transects where no target species was detected

absence_transects <- naboisho %>% distinct(Sample.Label, Date) %>% anti_join(., giraffe_transects, copy = T) %>% semi_join(naboisho, ., by=c(.Label,), copy=T) %>% group_by(Sample.Label, Date) %>% slice(1) %>% mutate(Species = , distance = NA, visit = NA, size = 0) %>% as.data.frame()

Joining, by = c(\Sample.Label\, \Date\)Factor `Date` contains implicit NA, consider using `forcats::fct_explicit_na`Factor `Date` contains implicit NA, consider using `forcats::fct_explicit_na`

rr # Combine missing transects with target species transects complete_transects <- naboisho %>% filter(species == , size <300) %>% rbind(absence_transects[,1:17]) # Plot a histograme of observation sizes with absence teansect complete_transects %>% ggplot(aes(x = size)) + geom_histogram(color = , binwidth = 1) + theme_minimal() + labs(x = group size, y = )

Now we see that by including the transects where Giraffe were not observed (zero-inflating the survey data) the histogram has changed significantly.

Collinearity

Collinearity is when covariates (the independent variables of a data set) are significantly correlated. This is typically a problem as collinearity will inflate the variance of modelled regression coefficients and therefore it is important to detect and remove the redundancy introduced, by dropping one of the covariates where the collinearity is observed. Failure to do this will increase standard errors of regression parameters and therefore inflate p-values [1^] when incorporating such data into regression models.

In order to demonstrate collinearity within independent variables, we can look at the doubs dataset3. This is a dataset that records fish species from a survey of the river Doubs in France, together with the spatial coordinates for the 30 survey sites and environmental covariates for each of the site. We can examine collinearity between the environmental covarites from doubs, by using a correlation matrix. This shows scatterplots between covarite data, together with Pearson coefficients.

library(PerformanceAnalytics) 
library(ade4)
#Let's load the data
data(doubs)
# Data representing water chemistry assay for each site...
env <- doubs$env
chart.Correlation(env, 
                  histogram=TRUE, 
                  method = "pearson",
                  pch=19,
                  title = "Correlation Matrix")

We can easily see that there are a number of covariates with significant (> c.90%) collinearity between. The ecological reasons for this correlation are outlined below.

Topological environmental covariates:

Water-chemistry environmental covariates:

For each of the covariates pairs outlined above, there is clearly a linear relationship between them. In order to avoid the side-effects mentioned above, we need to select the smallest subset of covariates that explains as much of the overall variation in the response variable(s). We will be exploring indpendent variable selection when fitting models for species distribution modelling.

Associations

Visualising the relationship between the response variable(s) and covariates can help to provide an initial understanding that may aid model selection technique, as well as highlighting observations that do not comply with the general pattern between response and covariate variables.

We can do this for the doubs data set. The plot below shows for each covariate (x axis), how the abundance for different species responds (y axis).

library(reshape2)
# Melt dataframe to give species data
species <- doubs$fish
species$site <- 1:30
df_melt_species <- melt(species, 
                        measure.vars = 1:28, 
                        id.vars = "site", 
                        value.name = "abundance", 
                        variable.name = "species")
  
environ <- doubs$env
environ$site <- 1:30
# Melt dataframe to give environmental data
df_melt_environ <- melt(environ, 
                        measure.vars = 1:11, 
                        id.vars = "site", 
                        value.name = "cov_value", 
                        variable.name = "env_cov")
# Now plot joined data to see response (species abundance) v covariates
# Begin by joining both melted datasets
full_join(df_melt_species,df_melt_environ,"site", keep = F) %>%
  # Remove site as a species column
  filter(species != "site") %>%
  
  ggplot() +
  # Plot abundance for against each covariate
  geom_point(aes( x = cov_value, y = abundance), size = 0.5) +
  # Fit a GAM for each plot and plot a smooth
  stat_smooth(aes( x = cov_value, 
                   y = abundance), 
              method = "gam", 
              # Use cubic splines as basis for GAM
              formula = y ~ s(x, bs = "cs"), 
              size = 0.5) +
  # Plot covariate by species - big grid!
  facet_grid(species ~ env_cov , scales="free") +
  theme_minimal() +
  # Flip x axis labels - otherwise they dont fit
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

We can see that although there is a lot of information here, we can see some clear types of behaviour for abundnace ~ covariate. For example, species abundance typically reduces as altitude increases, for all species types. If we look at dfs (distance from source), we can see that there are some species that have peak abundnace midstream (Cogo and Satr), where as most othere species have maximum abundance downstream, at the mouth of the river. We can see that species abundance is realtively high at low levels of ammonimum for all species, but abunance is zero for anything above 50 mgL\(^-1\)

Why isn’t pH plotting?!

References


  1. Zuur AF, Leno EN, Elphick CS (2010) A protocol for data exploration to avoid common statistical problems. Methods in Ecology and Evolution

  2. Zuur AF, Leno EN, Elphick CS (2010) A protocol for data exploration to avoid common statistical problems. Methods in Ecology and Evolution

  3. Verneaux, J. (1973) Cours d’eau de Franche-Comté (Massif du Jura). Recherches écologiques sur le réseau hydrographique du Doubs. Essai de biotypologie. Thèse d’état, Besançon. 1–257. Doubs river fish communities. https://www.davidzeleny.net/anadat-r/doku.php/en:data:doubs

  4. https://www.eea.europa.eu/data-and-maps/indicators/freshwater-quality/freshwater-quality-assessment-published-may-2

  5. https://www.eea.europa.eu/data-and-maps/indicators/freshwater-quality/freshwater-quality-assessment-published-may-2

LS0tCnRpdGxlOiAiQSBQcm90b2NvbCBmb3IgRGF0YSBFeHBsb3JhdGlvbiIKYXV0aG9yOiAiQW50aG9ueSBXYWl0ZSIKZGF0ZTogIjIwLzAxLzIwMjAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCi0tLQoKPCEtLSBMZXQncyByZWppZyB0aGUgc3R5bGUgc2hlZXQgYXMgdGhlIGRlZmF1bHQgaXNudCB2ZXJ5IGdvb2QgLS0+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgpib2R5eyAvKiBOb3JtYWwgICovCiAgICAgIGZvbnQtc2l6ZTogMTRweDsKICB9CnRkIHsgIC8qIFRhYmxlICAqLwogIGZvbnQtc2l6ZTogMTBweDsKfQpoMS50aXRsZSB7CiAgZm9udC1zaXplOiAyOHB4OwogIGNvbG9yOiBEYXJrUmVkOwp9CmgxIHsgLyogSGVhZGVyIDEgKi8KICBmb250LXNpemU6IDI4cHg7CiAgY29sb3I6IERhcmtCbHVlOwp9CmgyIHsgLyogSGVhZGVyIDIgKi8KICAgIGZvbnQtc2l6ZTogMjJweDsKICBjb2xvcjogRGFya0JsdWU7Cn0KaDMgeyAvKiBIZWFkZXIgMyAqLwogIGZvbnQtc2l6ZTogMThweDsKICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICBjb2xvcjogRGFya0JsdWU7Cn0KY29kZS5yeyAvKiBDb2RlIGJsb2NrICovCiAgICBmb250LXNpemU6IDEycHg7Cn0KcHJlIHsgLyogQ29kZSBibG9jayAtIGRldGVybWluZXMgY29kZSBzcGFjaW5nIGJldHdlZW4gbGluZXMgKi8KICAgIGZvbnQtc2l6ZTogMTRweDsKfQo8L3N0eWxlPgoKIyBJbnRyb2R1Y3Rpb24KVW5kZXJ0YWtpbmcgYSBzeXN0ZW1hdGljIGRhdGEgZXhwbG9yYXRpb24gcHJpb3IgdG8gYXBwbHlpbmcgc3RhdGlzdGljYWwgYW5hbHlzaXMgdGVjaG5pcXVlcyB0byBleHBlcmltZW50YWwgZGF0YSBjYW4gaGVscCBpbXByb3ZlIHRoZSBzdGF0aXN0aWNhbCB2YWxpZGl0eSBvZiByZXN1bHRzIGFuZCBhc3NvY2lhdGVkIGNvbmNsdXNvbnMuIEtleSB0byB0aGlzIGlzIGEgcHJvdG9jb2wgZm9yIGVuc3VyaW5nIHRoZSBzY2llbnRpc3QgZG9lcyBub3QgZGlzY292ZXIgYSBmYWxzZSBjb3ZhcmlhdGUgZWZmZWN0ICgqdHlwZSBJIGVycm9yKikgb3Igd3JvbmdseSBkaXNtaXNzIGEgbW9kZWwgd2FzIHZhbGlkIGNvdmFyaWF0ZSAoKnR5cGUgSUkgZXJyb3IqKS4KClRoZSBhaW0gb2YgdGhpcyB2aWduZXR0ZSBpcyB0byBwcm92aWRlIGEgcHJvdG9jb2wgZm9yIGRhdGEgZXhwbG9yYXRpb24gdGhhdCBpZGVudGlmaWVzIHBvdGVudGlhbCBwcm9ibGVtcyBiZWZvcmUgYW55IHN0YXRpc3RpY2FsIGFuYWx5c2lzIGlzIHVuZGVydGFrZW4uIFRoZSBhcHByb2FjaCBvdXRsaW5lZCBtYWtlcyBleHRlbnNpdmUgdXNlIG9mIHRoZSBtYXRlcmlhbCBkaXNjdXNzZWQgaW4gW14xXS4gRGF0YXNldHMgYXJlIHNvdXJjZWQgZnJvbSB0aGUgSW50ZXJuZXQgb3IgYWNhZGVtaWNzIGF0IE94Zm9yZCBCcm9va2VzIFVuaXZlcnNpdHkuCgpUaGUgZGF0YSBleHBsb3JhdGlvbiBwcm90b2NvbCBvdXRsaW5lIGlzIGFzIGZvbGxvd3M6CgoxLiAqKk91dGxpZXJzKiogLSBvYnNlcnZhdGlvbnMgdGhhdCBhcmUgcmVsYXRpdmVseSBsYXJnZSBvciBzbWFsbCB3aGVuIGNvbXBhcmVkIHRvIHRoZSBtYWpvcml0eSBvZiBvYnNlcnZhdGlvbnMsIGNhbiBzaWduaWZpY2FudGx5IGFmZmVjdCBhIHN0YXRpc2ljYWwgcmVzdWx0LgoyLiAqKkhvbW9nZW5laXR5IG9mIHZhcmlhbmNlKiogLSBhIG51bWJlciBvZiBzdGF0aXN0aWNhbCB0ZWNobmlxdWVzIGFzc3VtZSBob21vZ2VuZWl0eSBvZiB2YXJpYW5jZSBvZiBkYXRhLiBJZiB0aGlzIGlzIHZpb2xhdGVkIHRoZSBudWxsIGh5cG90aGVzaXMgbWF5YmUgZmFsc2VseSByZWplY3RlZCBvciB0aGUgcG93ZXIgb2YgdGhlIHRlc3QgbWF5YmUgZGVjcmVhc2UuCjMuICoqTm9ybWFsaXR5KiogLSBpZiBkYXRhIGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIGEgbnVtYmVyIG9mIHN0YXRpc3RpY2FsIHRlc3RzIG1heSBub3QgYmUgdmFsaWQsIGFuZCBhbHRlcm5hdGl2ZSBhcHByb2FjaGVzIHdpbGwgYmUgcmVxdWlyZWQKNC4gKipaZXJvLWluZmxhdGlvbioqIC0gd2hlbiBtb2RlbGxpbmcgc3BlY2llcyBjb3VudHMsIGEgbGFyZ2UgcHJvcG9ydGlvbiBvZiB0aGUgZGF0YSBtYXliZSB6ZXJvcy4gSW4gc3VjaCBjYXNlcyBhbHRlcm5hdGl2ZSBzdGF0aXN0aWNhbCBtb2RlbHMgc2hvdWxkIGJlIHVzZWQgdG8gYXZvaWQgYmlhc2VkIGVzdGltYXRlcy4KNS4gKipDb2xsaW5lYXJpdHkqKiAtIGF2b2lkaW5nIGNvcnJlbGF0aW9uIGJldHdlZW4gY292YXJpYXRlcyBpcyBpbXBvcnRhbnQgb3RoZXJ3aXNlIHN0YXRpc3RpY2FsIG91dGNvbWVzIG1heSBub3QgYmUgZGlyZWN0bHkgYXNzb2NpYXRlZCB3aXRoIHNwZWNpZmljIGNvdmFyaWF0ZXMuCjYuICoqQXNzb2NpYXRpb25zKiogLSBpbiB1bml2YXJpYXRlIGFuYWx5c2lzIGl0IGlzIGltcG9ydGFudCB0byBleHBsb3JlIHRoZSBpbmRpY2F0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHJlc3BvbnNlIHZhcmlhYmxlcyBhbmQgY292YXJpYXRlcy4KCgojIEFyZSB0aGVyZSBvdXRsaWVycz8KCkluIHNvbWUgc3RhdGlzdGljYWwgdGVjaG5pcXVlcyB0aGUgcmVzdWx0cyBhcmUgZG9taW5hdGVkIGJ5IG91dGxpZXJzOyBvdGhlciB0ZWNobmlxdWVzIHRyZWF0IHRoZW0gbGlrZSBhbnkgb3RoZXIgdmFsdWUuIEZvciBleGFtcGxlLCBvdXRsaWVycyBtYXkgY2F1c2Ugb3ZlcmRpc3BlcnNpb24gaW4gYSBQb2lzc29uIEdMTS4gT3V0bGllcnMgYXJlIGRlZmluZWQgYXMgYW4gb2JzZXJ2YXRpb24gdGhhdCBoYXMgYSByZWxhdGl2ZWx5IGxhcmdlIG9yIHNtYWxsIHZhbHVlIGNvbXBhcmVkIHRvIHRoZSBtYWpvcml0eSBvZiBvYnNlcnZhdGlvbnMuCgpJbiBvcmRlciB0byBkZW1vbnN0cmF0ZSB0aGUgZGF0YSBleHBsb3JhdGlvbiBzdGVwIHdlIHdpbGwgdXNlIGRhdGEgZnJvbSBzcGVjaWVzIGdyb3VwIHNpemVzIGluIHRoZSBOYWJvaXNobyBDb25zZXJ2YW5jeSB3aXRoaW4gS2VueWEuIEhlcmUgcmVzZWFyY2hlcnMgcmVjb3JkZWQgdGhlIHNpemUgb2Ygc3BlY2llcyBncm91cHMgYXMgdGhleSBkcm92ZSBhbG9uZyBhIG51bWJlciBvZiAya20gdHJhbnNlY3RzLgoKYGBge3IsIGxvYWRfbmFib2lzaG9fZGF0YSwgZWNobz1GLCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0Kcm0obGlzdCA9IGxzKCkpICAKc2V0d2QoIi9Vc2Vycy9hbnRob255L0RvY3VtZW50cy9HaXRIdWIvQ29tcHV0YXRpb25hbEVjb2xvZ3kvIikKbmFib2lzaG8gPC0gcmVhZC5jc3YoImRhdGFfYW5hbHlzaXNfZmlsZXMvTmFib2lzaG9fdGFyZ2V0X2Zvcm1hdC5jc3YiKQoKIyBMb2FkIGxpYnMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQpgYGAKCiMjIEJveHBsb3RzCgpUaGUgYm94cGxvdCB2aXN1YWxpemVzIHRoZSBtZWRpYW4gb2YgdGhlIGRhdGEgYW5kIHRoZSBzcHJlYWQgb2YgdGhlIGRhdGEgYWJvdXQgaXQuIEluIHRoZSBjaGFydCBiZWxvdyB0aGU6CgoqIE1lZGlhbiBpcyBwcmVzZW50ZWQgYXMgYSB2ZXJ0aWNhbCBsaW5lIHdpdGhpbiB0aGUgd2hpdGUgYm94CiogMjUlIGFuZCA3NSUgcGVyY2VudGlsZXMgZm9ybSBhIGJveCBhcm91bmQgdGhlIG1lZGlhbiB0aGF0IGNvbnRhaW5zIGhhbGYgb2YgdGhlIG9ic2VydmF0aW9ucwoqIEVuZHMgb2YgdGhlIHRoaW4gbGluZSBlaXRoZXIgc2lkZSBvZiB0aGUgd2hpdGUgYm94IGFyZSB0aGUgdXBwZXIgYW5kIGxvd2VyIHdoaXNrZXJzCiogVXBwZXIgd2hpc2tlciBjb3ZlcnMgdmFsdWVzIG5vIGxhcmdlciAob3Igc21hbGxlcikgdGhhbiB0aGUgaW50ZXItcXVhcnRpbGUgcmFuZ2UgKHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBmaXJzdCBhbmQgdGhpcmQgcXVhcnRpbGVzKQoqIERhdGEgYmV5b25kIHRoZSB3aGlza2VycyBhcmUgdGhlIG91dGxpZXJzLCBhbmQgYXJlIHNob3duIGFzIHJlZCBkb3RzLgoKYGBge3IsIG5hYm9pc2hvX2JveF9wbG90LCB3YXJuaW5nPUYsIG1lc3NhZ2U9RiwgZmlnLmFsaWduPSdjZW50ZXInfQpuYWJvaXNobyAlPiUgCiAgZmlsdGVyKHNwZWNpZXMgIT0gIk5vbmUiKSAlPiUKICBnZ3Bsb3QoYWVzKHg9c3BlY2llcywgeT1sb2cxMChzaXplKSkpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXI9InJlZCIsIAogICAgICAgICAgICAgICBvdXRsaWVyLnNoYXBlPTE2LAogICAgICAgICAgICAgICBvdXRsaWVyLnNpemU9MC43NSwgCiAgICAgICAgICAgICAgIG5vdGNoPUYpICsKICBjb29yZF9mbGlwKCkgKwogIGdndGl0bGUoIlNwZWNpZXMgZ3JvdXAgc2l6ZXMgaW4gTmFib2lzaG8gQ29uc2VydmFuY3kuIDIwMTYgdG8gMjAxOSIpICsKICB4bGFiKCJTcGVjaWVzIikgKwogIHlsYWIoIlNwZWNpZXMgZ3JvdXAgc2l6ZSAtIGxvZzEwIikgKwogIHRoZW1lX2J3KCkKYGBgCgpOb3RlIHRoZSB4IGF4aXMgaXMgc2NhbGVkIHRvIGxvZyBiYXNlIDEwLCBpbiBvcmRlciB0byBjb21wYXJlIHNwZWNpZXMgd2l0aCBzbWFsbCBncm91cCBzaXplcywgc3VjaCBhcyBCbGFjayBCYWNrZWQgSmFja2FsLCBhZ2FpbnN0IHNwZWNpZXMgd2l0aCBsYXJnZSBncm91cCBzaXplcyBzdWNoIGFzIFplYnJhIG9yIFdpbGRlYmVlc3QuCgojIyBDbGV2ZWxhbmQgZG90IHBsb3QKCioqQ2xldmVsYW5kIGRvdHBsb3QqKiBpcyBhIGNoYXJ0IGluIHdoaWNoIHRoZSByb3cgbnVtYmVyIG9mIGFuIG9ic2VydmF0aW9uIGlzIHBsb3R0ZWQgdnMuIHRoZSBvYnNlcnZhdGlvbiB2YWx1ZSwgdGhlcmVieSBwcm92aWRpbmcgYSBtb3JlIGRldGFpbGVkIHZpZXcgb2YgaW5kaXZpZHVhbCBvYnNlcnZhdGlvbnMgdGhhbiBhIGJveHBsb3QgYWxsb3dzLiBQb2ludHMgdGhhdCBzdGljayBvdXQgb24gdGhlIHJpZ2h0LWhhbmQgc2lkZSwgb3Igb24gdGhlIGxlZnQtaGFuZCBzaWRlLCBhcmUgb2JzZXJ2ZWQgdmFsdWVzIHRoYXQgYXJlIGNvbnNpZGVyYWJsZSBsYXJnZXIsIG9yIHNtYWxsZXIsIHRoYW4gdGhlIG1ham9yaXR5IG9mIHRoZSBvYnNlcnZhdGlvbnMsIGFuZCByZXF1aXJlIGZ1cnRoZXIgaW52ZXN0aWdhdGlvbi4gCgpUaGUgY2hhcnQgYmVsb3cgc2hvd3MgYSBkb3QgcGxvdCBmb3IgZWFjaCBzcGVjaWVzIHdpdGhpbiB0aGUgTmFib2lzaG8gZGF0YXNldCBmcm9tIDIwMTYgdG8gMjAxOS4KCmBgYHtyIGNsZXZlbGFuZF9kb3RfYWxsX3NwZWNpZXMsIGVjaG89VCwgd2FybmluZz1GLCBtZXNzYWdlPUYsIGZpZy5hbGlnbj0iY2VudGVyIiwgZmlnLmhlaWdodD0yNX0KbmFib2lzaG8gJT4lCiAgZmlsdGVyKHNwZWNpZXMgIT0gIk5vbmUiKSAlPiUgIyBSZW1vdmUgc3BlY2llcyB3aXRoIG5vIG5hbWUhCiAgZ2dwbG90KGFlcyh4PXNpemUsIHk9c2VxKDEsIGxlbmd0aChzaXplKSkpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjMpICsKICBnZ3RpdGxlKCJDbGV2ZWxhbmQgZG90IHBsb3Qgb2YgTmFib2lzaG8gc3BlY2llcyAyMDE2IHRvIDIwMTkiKSArCiAgbGFicyh4ID0gIk9ic2VydmVkIGdyb3VwIHNpemUgb2YgR2lyYWZmZSIsIAogICAgICAgeSA9ICJPcmRlciBvZiBkYXRhIikgKwogIHRoZW1lX2J3KCkgKwogIGZhY2V0X3dyYXAofnNwZWNpZXMsIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZV94IikKYGBgCgoKRm9yIEdpcmFmZmUgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGEgc2luZ2xlIG91dGxpZXI7IGEgZ3JvdXAgc2l6ZSBvZiAzNTAgR2lyYWZmZS4gVGhpcyBpcyBxdWl0ZSBjbGVhcmx5IGEgZGF0YSBlbnRyeSBlcnJvci4gSWYgd2UgcmVtb3ZlIHRoaXMgb3V0bGllciBhbmQgcmVwbG90IHRoZSBkb3QgcGxvdCwgd2Ugc2VlIHRoZSBmb2xsb3dpbmc6CgpgYGB7ciBjbGV2ZWxhbmRfZG90X2dpcmFmZmVfY2xlYW4sIGVjaG89VCx3YXJuaW5nPUYsIG1lc3NhZ2U9RiwgZmlnLmFsaWduPSJjZW50ZXIifQpuYWJvaXNobyAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSAiR2lyYWZmZSIpICU+JQogIGZpbHRlcihzaXplIDw9IDMwMCkgJT4lCiAgZ2dwbG90KGFlcyh4PXNpemUsIHk9c2VxKDEsIGxlbmd0aChzaXplKSkpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjMpICsKICBnZ3RpdGxlKCJDbGV2ZWxhbmQgZG90IHBsb3Qgb2YgR2lyYWZmZSBOYWJvaXNobyAyMDE2IHRvIDIwMTkgLSBvdXRsaWVycyByZW1vdmVkIikgKwogIGxhYnMoeCA9ICJPYnNlcnZlZCBncm91cCBzaXplIG9mIEdpcmFmZmUiLCAKICAgICAgIHkgPSAiT3JkZXIgb2YgZGF0YSIpICsKICB0aGVtZV9idygpCmBgYAoKTm90ZSB3ZSBhbHNvIGhhdmUgYSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHdpdGggZ3JvdXAgc2l6aW5nIG9mIHplcm8uIFRoaXMgaXMgY2xlYXJseSBub3QgcG9zc2libGUgYW5kIGlzIHByb2JhYmx5IGR1ZSB0byBhIG1pc3VuZGVyc3RhbmRpbmcgb24gYmVoYWxmIG9mIHRoZSBkYXRhIHJlY29yZGVyLCBhbmQgc28gd2UgY2FuIGNvcnJlY3QgdGhpcyBtaXN0YWtlIGFjY29yZGluZ2x5OgoKYGBge3IgcmVwbGFjZV9zaXplX3dpdGhfemVyb3MsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm5hYm9pc2hvIDwtIG5hYm9pc2hvICU+JQogIG11dGF0ZV9hbGwoZnVucyhyZXBsYWNlKC4sIHNpemUgPT0gMCwgMSkpKQpgYGAKCiMjIEltcGxpY2F0aW9ucwoKSGF2aW5nIGRldGVybWluZWQgdGhhdCB0aGVyZSBhcmUgb3V0bGllcnMgaW4gb2JzZXJ2YXRpb25zLCB3ZSBjYW4gdGFrZSBhIG51bWJlciBvZiBzdGVwcyB0byB2YWxpZGF0ZSB0aGVpciBpbmNsdXNpb24gb3Igbm90OgoKKiBjaGVjayB0aGUgZGF0YSB0byBlbnN1cmUgdGhhdCB0aGVyZSBoYXMgbm90IGJlZW4gYSBkYXRhIGVudHJ5IGVycm9yCiogY2hlY2sgdGhlIHNjaWVudGlmaWMgbGl0ZXJhdHVyZSB0byBwcm92aWRlIGFuIGV4cGVjdGVkIHZhbHVlLgoqIGdlbmVyYXRlIGEgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyByYW5kb21seSBmcm9tIGFuIGFwcHJvcHJpYXRlIGRpc3RyaWJ1dGlvbiwgYW5kIGRldGVybWluZSBob3cgdGhlIHByb3BvcnRpb24gb2YgZXh0cmVtZW0gcG9pbnRzIGNvbXBhcmVzIHRvIHRoZSBmaWVsZCBkYXRhCgpJZiB0aGUgb3V0bGllcnMgYXJlIGRlZW1lZCB2YWxpZCB0aGVuIGl0IG1heWJlIG5lY2Vzc2FyeSB0byBvbmx5IHVzZSBzdGF0aXN0aWNhbCBtZXRob2RzIHRoYXQgY2FuIGhhbmRsZSBzdWNoICpvdmVyLWRpc3BlcnNlZCogZGF0YS4gRm9yIGV4YW1wbGUsIFBvaXNzb24gb3IgbmVnYXRpdmUtYmlub21pYWwgZGlzdHJpYnV0aW9ucyBmb3IgY291bnQgZGF0YS4KCgojIEhvbW9nZW5laXR5IG9mIHZhcmlhbmNlCgpIb21vZ2VuZWl0eSBvZiB2YXJpYW5jZSBpcyBhbiBpbXBvcnRhbnQgYXNzdW1wdGlvbiBpbiBhbmFseXNpcyBvZiB2YXJpYW5jZSAoQU5PVkEpLiBUaGUgcGxvdCBiZWxvdyBzaG93cyBhIGJveHBsb3QgZm9yIFdpbGRlYmVzc3QgZ3JvdXAgc2l6ZSBvYnNlcnZhdGlvbnMgd2l0aGluIHRoZSBOYWJvaXNobyBkYXRhc2V0LiBJbiB0aGUgcGxvdHMgYmVsb3cgd2UgaGF2ZSBhc3N1bWVkIHRoYXQgdGhlIGxvbmcgcmFpbnMgYXJlIGZyb20gQXByaWwgdG8gTWF5LCB0aGUgc2hvcnQgcmFpbnMgYXJlIGluIE5vdmVtYmVyIGFuZCBhbGwgb3RoZXIgbW9udGhzIGFyZSB0aGUgZHJ5IHNlYXNvbi4KClRvIGFwcGx5IEFOT1ZBIHRvIHRoZXNlIGRhdGEgdG8gZGV0ZXJtaW5lIGlmIG1lYW4gV2lsZGViZWVzdCBncm91cCBzaXplIHZhcmllcyBieSB5ZWFyLCBzZWFzb24gb3IgYSBjb21iaW5hdGlvbiBvZiB5ZWFyIGFuZCBzZWFzb24sIHdlIG11c3QgYXNzdW1lIHRoYXQgdmFyaWF0aW9uOgoKMS4gSW4gdGhlIHllYXJzIGFyZSBzaW1pbGFyCjIuIEluIG9ic2VydmF0aW9ucyBmb3IgZWFjaCBzZWFzb24gYXJlIHNpbWlsYXIKMy4gQmV0d2VlbiBzZWFzb25zIHdpdGhpbiB5ZWFycyBhcmUgc2ltaWxhci4KCmBgYHtyIHdpZGViZWVzdF9ieV9zZWFzb24sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RiwgZmlnLmFsaWduPSAnY2VudGVyJ30KIyBBZGQgYSBjb2x1bW4gdG8gdGhlIGRhdGEgc2V0IHRvIGRldGVybWluZSBtb250aCBvZiB5ZWFyCgpuYWJvaXNobyA8LSBuYWJvaXNobyAlPiUgCiAgbXV0YXRlKE1vbnRoID0gbW9udGgoRGF0ZSwgbGFiZWw9IFQsIGFiYnIgPSBUKSkgJT4lCiAgIyBBZGQgYSBzZWFzb24gY29sdW1uIHRvIGRhdGEKICBtdXRhdGUoc2Vhc29uID0gY2FzZV93aGVuKAogICAgTW9udGggPT0gIkFwciIgfiAibG9uZyByYWlucyIsICAgICAgICAgICAjbG9uZyByYWlucwogICAgTW9udGggPT0gIk1heSIgfiAibG9uZyByYWlucyIsICAgICAgICAgICAjbG9uZyByYWlucwogICAgTW9udGggPT0gIk5vdiIgfiAic2hvcnQgcmFpbnMiLCAgICAgICAgICAgI3Nob3J0IHJhaW5zCiAgICBUUlVFICAgICAgICAgICB+ICJkcnkiKSkgIyBFbHNlIGl0J3MgdGhlIGRyeSBzZWFzb24KICAKd2lsZGViZWVzdF9zZWFzb25zIDwtIG5hYm9pc2hvICU+JSAgCiAgIyBUYWtlIFdpZGViZWVzdCBvbmx5LCBhbmQgcmVtb3ZlIG91dGxpZXJzIGZvciBncm91cCBzaXplID4gNTAwCiAgZmlsdGVyKHNwZWNpZXMgPT0gIldpbGRlYmVlc3QiLCBzaXplIDwgNTAwKSAlPiUKICAjIEZpbHRlciBvdXQgMjAxNiBhcyBpbnN1ZmZpY2llbnQgZGF0YSB0byBkZXRlcm1pbmUgbW9udGgKICBmaWx0ZXIoeWVhciAhPSAyMDE2KQoKd2lsZGViZWVzdF9zZWFzb25zICU+JQogICMgUGxvdCBib3hwbG90cyBieSBzZWFzb24sIGRvbid0IGRpc3BsYXkgb3V0bGllcnMKICBnZ3Bsb3QoYWVzKHg9c2Vhc29uLCB5PXNpemUpKSArCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZT1OQSkgKwogICAgIyBGYWNldCBieSB5ZWFyCiAgICBmYWNldF93cmFwKC4gfiB5ZWFyKSArCiAgICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsIDkwKSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZ3RpdGxlKCJCb3hwbG90IG9mIFdpbGRlYmVlc3QgZ3JvdXAgc2l6ZSBieSBzZWFzb24sIGFjcm9zcyB5ZWFycyAobm8gb3V0bGllcnMpIikgKwogICAgbGFicyh4ID0gIlNlYXNvbiBhbmQgeWVhciIsIAogICAgICAgICB5ID0gIk9ic2VydmVkIGdyb3VwIHNpemUgb2YgV2lsZGViZWVzdCIpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoZSB2YXJpYW5jZSBiZXR3ZWVuIHNlYXNvbnMgd2l0aGluIGEgeWVhciBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmb3IgMjAxNyBhbmQgMjAxOS4gVGhlcmVmb3JlIHdlIHNob3VsZCBub3QgdW5kZXJ0YWtlIHRoZSBjbGFzc2ljIEFOT1ZBIHRlc3QgaWYgd2Ugd2FudCB0byBhbmFseXNlIGRpZmZlcmVuY2VzIGluIGdyb3VwIG1lYW5zIHdpdGhpbiB0aGUgeWVhcnMgMjAxNyBhbmQgMjAxOS4gQWxzbyB3ZSBoYXZlIG1vcmUgdGhhbiB0d28gZ3JvdXBzIGFuZCBiZWNhdXNlIHRoZSBzaXplIGRhdGEgYXJlIGNvdW50cywgd2Ugc2hvdWxkIG5vdCBhc3N1bWUgdGhleSBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuCgojIyBMZXZlbm5lJ3MgaG9tb2dlbnJpdHkgb2YgdmFyaWFuY2UgdGVzdAoKV2UgY2FuIGRpcmVjdGx5IHRlc3QgZm9yIGhvbW9nZW5laXR5IG9mIHZhcmlhbmNlcyBieSB1c2luZyAqTGV2ZW5uZSdzIHRlc3QqOgoKYGBge3Igd2lsZGViZWVzdF9sZXZlbm5lLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNhcikKCiMgR2VuZXJhdGUgYSB0d28td2F5IHRlc3QgdG8gaW5jb3Jwb3JhdGUgaW50ZXJhY3Rpb24gYmV0d2VlbiBzZWFzb25zIGFuZCB5ZWFycwpsZXZlbmVUZXN0KHNpemUgfiBhcy5mYWN0b3Ioc2Vhc29uKSAqIGFzLmZhY3Rvcih5ZWFyKSwgZGF0YSA9IHdpbGRlYmVlc3Rfc2Vhc29ucykKCmBgYAoKRnJvbSB0aGUgb3V0cHV0IGFib3ZlIHdlIGNhbiBzZWUgdGhhdCB0aGUgcC12YWx1ZSBpcyBtdWNoIGxlc3MgdGhhbiB0aGUgc2lnbmlmaWNhbmNlIGxldmVsIG9mIDAuMDUuIFRoaXMgbWVhbnMgdGhhdCB3ZSBjYW4gbm90IGFzc3VtZSBlcXVhbCB2YXJpYW5jZXMgYmV0d2VlbnMgeWVhcnMgYW5kIHNlYXNvbnMuIAoKV2UgdGhlcmVmb3JlIG5lZWQgdG8gdXNlIGEgdGVzdCB0aGF0IGRvZXMgbm90IGFzc3VtZSBlcXVhbCB2YXJpYW5jZSwgY2FuIHdvcmsgZm9yIHRocmVlIGRpZmZlcmVudCBncm91cHMgYW5kIGlzIG5vbi1wYXJhbWV0cmljLiBUaGUgKktydXNrYWwtV2FsbGlzKiB0ZXN0IGlzIGEgbm9ucGFyYW1ldHJpYyAoZGlzdHJpYnV0aW9uIGZyZWUpIHRlc3QsIGFuZCBpcyB1c2VkIHdoZW4gdGhlIGFzc3VtcHRpb25zIG9mIG9uZS13YXkgQU5PVkEgYXJlIG5vdCBtZXQuIFRoZSB0ZXN0IGNhbiBhc3Nlc3MgZm9yIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIG9uIGEgY29udGludW91cyBkZXBlbmRlbnQgdmFyaWFibGUgKGluIG91ciBjYXNlLCBncm91cCBzaXplKSBieSBhIHR3byBvciBtb3JlIGNhdGVnb3JpY2FsIGluZGVwZW5kZW50IHZhcmlhYmxlIGdyb3VwcyAoaW4gb3VyIGNhc2UgdGhlIHRocmVlIGRpZmZlcmVudCBzZWFzb25zKS4gV2l0aCB0aGlzIHRlc3Qgd2UgY2FuIGFzayB0aGUgcXVlc3Rpb24gaWYgdGhlIG1lYW4gZ3JvdXAgc2l6ZSBiZXR3ZWVuIGxvbmcgcmFpbnMsIHNob3J0IHJhaW5zIGFuZCBkcnkgc2Vhc29ucyBpcyBzdGF0aXN0aWNhbGx5IGRpZmZlcmVudDoKCmBgYHtyIGtydXNrYWwudGVzdH0KIyBTZWxlY3QgMjAxNyB3aWxkZWJlZXN0IG9ic2VydmF0aW9ucyBvbmx5CndpbGRlYmVlc3Rfc2Vhc29ucyAlPiUgZmlsdGVyKHllYXIgPT0gMjAxNykgJT4lIAogIGtydXNrYWwudGVzdCguJHNpemUsIC4kc2Vhc29uLCBkYXRhID0gLikKCmBgYAoKQXMgcCA8PCAwLjA1LCB3ZSBjYW4gdGhlcmVmb3JlIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgdGhlIGRpZmZlcmVuY2UgaW4gbWVhbnMgaXMgemVyby4gU28gdGhlIG1lYW4gZ3JvdXAgc2l6ZSBkb2VzIHZhcnkgYnkgc2Vhc29uIGluIDIwMTcuIEJ1dCBiZXR3ZWVuIHdoaWNoIGdyb3VwcyBpcyB0aGUgZGlmZmVybmNlIG1vc3Qgc2lnbmlmaWNhbnQ/IFRvIGFuc3dlciB0aGlzIHdlIG5lZWQgYSBwb3N0LWhvYyB0ZXN0LCBhbmQgZm9yIHRoaXMgd2UgdXNlIGEgcGFpcndpc2Ugd2lsY294IHRlc3Q6CgpgYGB7ciBwdy53aWxjb3MudGVzdH0Kd2lsZGViZWVzdF9zZWFzb25zICU+JSBmaWx0ZXIoeWVhciA9PSAyMDE3KSAlPiUgCiAgcGFpcndpc2Uud2lsY294LnRlc3QoLiRzaXplLCAuJHNlYXNvbiwgZGF0YT0uKQpgYGAKClRoZSByZXN1bHRzIHNob3cgdXMgdGhhdCB0aGUgbGVhc3Qgc2lnbmlmaWNhbnQgZGlmZmVybmNlIGluIDIwMTcgd2FzIGJldHdlZW4gdGhlIGRyeSBhbmQgc2hvcnQgcmFpbnMgc2Vhc29ucywgYXMgY29uZmlybWVkIGJ5IHRoZSBib3hwbG90cyBhYm92ZS4gV2UgY2FuIGFwcGx5IHRoZSBLcnVza2FsLVdhbGxpcyB0ZXN0IGFjcm9zcyB0aGUgZW50aXJlIG5hYm9pc2hvIGRhdGFzZXQuIFRoc2kgdGltZSB3ZSdsbCB0ZXN0IGlmIHllYXIgb24geWVhciBtZWFuIGdyb3VwIHNpemVzIGNoYW5nZXMgYXJlIHNpZ25pZmljYW50OgoKYGBge3IgdGVzdF9hbGxfc3BlY2llcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBBcHBseSBLcnVza2FsLVdhbGxpcyB0byBlYWNoIHNwZWNpZXMgdG8gc2VlIGlmIGNoYW5nZSBpbiBtZWFuIHllYXIgb24geWVhciBpcyBzaWduaWZpY2FudAoKc3BlY2llc19rd190ZXN0IDwtIG5hYm9pc2hvICU+JSAKICAjIEZpbHRlciBzcGVjaWVzIHJlY29yZGVkIGFzIE5vbmUKICBmaWx0ZXIoc3BlY2llcyAhPSAiTm9uZSIpICU+JQogICMgQ291bnQgdGVzdGFibGUgZGF0YQogIGNvdW50KHNwZWNpZXMsIHllYXIpICU+JQogICMgUmVtb3ZlIGFueSBkYXRhIGxlc3Mgd2l0aCBhIGNvdW50IGxlc3MgdGhhbiAxMAogIGZpbHRlcihuPjEwKSAlPiUKICAjIENyZWF0ZSB0ZXN0YWJsZSBkYXRhIHNldAogIGxlZnRfam9pbihuYWJvaXNobykgJT4lCiAgIyBOZXN0IGVhY2ggc3BlY2llcyBncm91cAogIG5lc3QoLXNwZWNpZXMpICU+JQogICMgTWFwIGVhY2ggZ3JvdXAgdG8gdGhlIEtydXNrYWwgdGVzdAogIG11dGF0ZShrcnVza2FsX3JlcyA9IG1hcChkYXRhLCB+IGtydXNrYWwudGVzdCgueCRzaXplLCAueCR5ZWFyKSksCiAgICAgICAgIGtydXNrYWwgICAgID0gbWFwKGtydXNrYWxfcmVzLCBicm9vbTo6dGlkeSkpICU+JQogICMgV2UgZG9udCBuZWVkZWQgdGhlIG5lc3RlZCBkYXRhIGZyYW1lcyBhbnltb3JlCiAgc2VsZWN0KC1kYXRhKSAlPiUKICAjIFVubmVzdCB0aGUgcmVzdWx0CiAgdW5uZXN0KGtydXNrYWwpCgojIFNlbGVjdCBzcGVjaWVzIHdoZXJlIHdlIGNhbm5vdCByZWplY3QgdGhlIG51bGwtaHlwb3RoZXNpcwpzcGVjaWVzX2t3X3Rlc3QgJT4lIAogIGZpbHRlciguJHAudmFsdWUgPiAwLjA1KQogIAoKCmBgYAoKVGhlIHRhYmxlIGFib3ZlIGxpc3RzIHRoZSBzcGVjaWVzIHdoZXJlIGNhbm5vdCBpbmZlciBhbnl0aGluZyBmcm9tIHllYXItb24teWVhciBtZWFuIGdyb3VwIHNpemUgY2hhbmdlcy4KCiMjIEltcGxpY2F0aW9ucwoKU3RhdGlzdGljYWwgbW9kZWxsaW5nIHVzaW5nIHJlZ3Jlc3Npb24gd2lsbCB0eXBpY2FsbHkgYXNzdW1lIGhvbW9nZW5vdXMgdmFyaWFuY2UuIFRoaXMgY2FuIGJlIGNoZWNrZWQgYnkgcGxvdHRpbmcgdGhlIG1vZGVsIHJlc2lkdWFscyBhZ2FpbnN0IHRoZSBmaXR0ZWQgdmFsdWVzLiBBIGdvb2QgZml0IHdpbGwgaGF2ZSBoYXZlIHNpbWlsYXIgcmVzaWR1YWwgdmFyaWF0aW9uIGFjcm9zcyBhbGwgdmFsdWVzLiBJbiBvcmRlciB0byBhZGRyZXNzIGhldGVyb2dlbmVpdHkgb2YgdmFyaWFuY2UgaW4gdGhlIHJlc3BvbnNlIHZhcmlhYmxlLCBpdCBpcyBwb3NzaWJsZSB0byB0cmFuc2Zvcm0gaXQ7IGZvciBleGFtcGxlIHZpYSBhIGxvZyB0cmFuc2Zvcm1hdGlvbi4gQWx0ZXJuYXRpdmVseSwgbW9kZWxsaW5nIGFwcHJvYWNoZXMgdGhhdCBkbyBub3QgYXNzdW1lIGVxdWFsIHZhcmlhbmNlIGNhbiBiZSB1c2VkLgoKCiMgTm9ybWFsaXR5CgpBIHNpZ25pZmljYW50IG51bWJlciBvZiBzdGF0aXN0aWNhbCBtb2RlbGxpbmcgdG9vbHMgYXNzdW1pbmcgdGhlIGRhdGEgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBGb3IgZXhhbXBsZSwgbGluZWFyIHJlZ3Jlc3Npb24gYXNzdW1lcyB0aGUgZGF0YSB1cyBub3JtYWxseSBkaXN0cmlidXRlZC4gCgojIyBIaXN0b2dyYW0KClRoZSBmb2xsb3dpbmcgcGxvdCBzaG93cyBhIGhpc3RvZ3JhbSBmb3IgdGhlIHdlaWdodCBvZiAxLDE5MyBzcGFycm93cyBbXjFdLgoKYGBge3IsIGVjaG89VCx3YXJuaW5nPUYsIG1lc3NhZ2U9RiwgZmlnLmFsaWduPSJjZW50ZXIifQojIExvYWQgc3BhcnJvd3MgZGF0YSBzZXQKc3BhcnJvd3MgPC0gcmVhZC5kZWxpbSgiL1VzZXJzL2FudGhvbnkvRG9jdW1lbnRzL0dpdEh1Yi9Db21wdXRhdGlvbmFsRWNvbG9neS9kYXRhX2FuYWx5c2lzX2ZpbGVzL1NwYXJyb3dzLnR4dCIpCgpnZ3Bsb3Qoc3BhcnJvd3MsIGFlcyh4ID0gd3QpKSArIAogIGdlb21faGlzdG9ncmFtKGNvbG9yID0gImJsYWNrIiwgYmlud2lkdGggPSAwLjI1KSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgbGFicyh4ID0gIldlaWdodCAoZykiLCAKICAgICAgICAgeSA9ICJGcmVxdWVuY3kiKQpgYGAKCiMjIERlbnNpdHkgcGxvdAoKV2hpc2x0IHRoZSBoaXN0b2dyYW1lIGFwcGVhcnMgdG8gaGF2ZSBhbiBhcHByb3hpbWF0ZSBub3JtYWwgc2hhcGUsIHdlIGNhbiBwbG90IGEgZGVuc2l0eSBwbG90IHRvIGhhdmUgYSBiZXR0ZXIgdmlzdWFsIGNoZWNrPwoKYGBge3IgZGVuc2l0eV9wbG90LCBmaWcuYWxpZ249J2NlbnRlcicsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2dwdWJyKQpnZ2RlbnNpdHkoZGF0YSA9IHNwYXJyb3dzJHd0LAogICAgICAgICAgbWFpbiA9ICJEZW5zaXR5IHBsb3Qgb2Ygc3BhcnJvdyB3ZWlnaHQiLAogICAgICAgICAgeGxhYiA9ICJTcGFycm93IHdlaWdodCIpIApgYGAKCiMjIFEtUSBQbG90CgpUaGUgbm9ybWFsIFEtUSBwbG90IGlzIGFuIGFsdGVybmF0aXZlIGdyYXBoaWNhbCBtZXRob2Qgb2YgYXNzZXNzaW5nIG5vcm1hbGl0eSB0byB0aGUgaGlzdG9ncmFtLiBUaGUgc2NhdHRlciBjb21wYXJlcyB0aGUgZGF0YSB0byBhCnBlcmZlY3Qgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gVGhlIHNjYXR0ZXIgc2hvdWxkIGxpZSBhcyBjbG9zZSB0byB0aGUgbGluZSBhcyBwb3NzaWJsZSB3aXRoIG5vIG9idmlvdXMgcGF0dGVybiBjb21pbmcgYXdheSBmcm9tIHRoZSBsaW5lIGZvciB0aGUgZGF0YSB0byBiZSBjb25zaWRlcmVkIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBUaGUgUS1RIHBsb3QgZm9yIHRoZSBzcGFycm93IHdlaWdodCBkYXRhIGlzIHBsb3R0ZWQgYmVsb3c6CgpgYGB7ciBxcV9wbG90LCBmaWcuYWxpZ249J2NlbnRlcid9CmdncXFwbG90KHNwYXJyb3dzJHd0KQpgYGAKCldlIGNhbiBzZWUgdGhhdCB0aGUgUS1RIHBsb3QgZGVwYXJ0cyBmcm9tIHRoZSB0aGVvcmV0aWNhbCBsaW5lIGF0IGV4dHJlbWVzLiBJZiB3ZSBzdWJzZXQgdGhlIGRhdGEgYnkgbW9udGggYW5kIHBsb3QgYSBzcGFycm93IHdlaWdodCBoaXN0b2dyYW1lIGZvciBlYWNoIG1vbnRoLCB3ZSBjYW4gc2VlIGhvdyB0aGUgZGF0YSB2YXJpZXMgY29uc2lkZXJhYmx5IHRocm91Z2hvdXQgdGhlIHllYXIuCgpgYGB7ciBzcHBfd2VpZ2h0X21vbnRoLCBmaWcuYWxpZ249J2NlbnRlcicsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNwYXJyb3dzICU+JQogIG11dGF0ZShNb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoTW9udGgsIGFiYnIgPSBULCBsYWJlbCA9IFQpKSAlPiUKZ2dwbG90KGFlcyh4ID0gd3QpKSArIAogIGdlb21faGlzdG9ncmFtKGNvbG9yID0gImJsYWNrIiwgYmlud2lkdGggPSAwLjI1KSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgbGFicyh4ID0gIldlaWdodCAoZykiLCAKICAgICAgICAgeSA9ICJGcmVxdWVuY3kiKSArCiAgZmFjZXRfd3JhcCguIH4gTW9udGgpCmBgYAoKVGhpcyBpcyBtb3N0IGxpa2VseSBkdWUgdG8gc2FtcGxpbmcgYmlhczsgbW9yZSBzYW1wbGVzIGR1cmluZyB0aGUgc3VtbWVyLiBJZiB3ZSBwbG90IGEgaGlzdG9yZ3JhbSBvZiB0aGUgbW9udGggdGhlIG9ic2VydmF0aW9uIHdhcyByZWNvcmRlZCB3ZSBkbyBpbmRlZWQgc2VlIGEgc2tldyB0b3dhcmRzIHN1bW1lciBtb250aHM6CgpgYGB7ciBzcGFycm93X21vbnRocywgZmlnLmFsaWduPSdjZW50ZXInfQpzcGFycm93cyAlPiUKICBtdXRhdGUoTW9udGggPSBsdWJyaWRhdGU6Om1vbnRoKE1vbnRoLCBhYmJyID0gVCwgbGFiZWwgPSBUKSkgJT4lCiAgZ2dwbG90KGFlcyhNb250aCwgZmlsbD1Nb250aCkpICsKICAgIGdlb21faGlzdG9ncmFtKHN0YXQ9ImNvdW50IikgKwogICAgdGhlbWVfYncoKQpgYGAKCiMjIEltcGxpY2F0aW9ucwoKSWYgdGVzdHMgc3VnZ2VzdCB0aGF0IHRoZSBkYXRhIGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgaXQgbWF5IGJlIHBvc3NpYmxlIHRvIGxvZyB0cmFuc2Zvcm0gKG9yIHRha2UgdGhlIHNxdWFyZSByb290KSB0aGUgcmVzcG9uc2UgdmFyaWFibGUgYW5kIHRoZW4gcmVwZWF0IHRoZSBub3JtYWxpdHkgY2hlY2tzLiBJZiB3ZSBuZWVkIHRvIHVuZGVydGFrZSBmdXJ0aGVyIHRlc3RzIHRoYXQgYXJlIGRlcGVkbmVudCBvbiBub3JtYWxpdHksIGZvciBleGFtcGxlIEFOT1ZBLCB0aGVuIHdlIGNhbiB1c2Ugbm9uIHBhcmFtZXRyaWMgdGVzdHMgc3VjaCBhcyB0aGUgKktydXNrYWwtV2FsbGlzIHRlc3QqIGZvciBhIG9uZS13YXkgQU5PVkEgdGVzdC4KCiMgWmVybyBpbmZsYXRpb24KCldoZW4gc3R1ZHlpbmcgc3BlY2llcyBwb3B1bGF0aW9ucyBlY29sb2dpc3RzIHdpbGwgb2Z0ZW4gcmVjb3JkIHNwZWNpZXMgY291bnQgZGF0YSwgYW5kIHRoZW4gc2VlayB0byBzdHVkeSBob3cgc3BlY2llcyBhYnVuZGFuY2UgdmFyaWVzIGFzIGEgZnVuY3Rpb24gb2YgY292YXJpYXRlcyBzdWNoIGFzIHRpbWUsIGhhYml0YXQgdHlwZSwgdGVtcGVyYXR1cmUsIHdlYXRoZXIgZXRjLiBJZiBzcGVjaWVzIGFyZSBub3Qgb2JzZXJ2ZWQgZHVyaW5nIGEgc3VydmV5LCB0aGlzIGlzIHJlY29yZGVkIGFzIGFuIGFic2VuY2UuIFRoaXMgY2FuIGxlYWQgdG8gb3Zlci1kaXNwZXJzaW9uIHdpdGhpbiB0aGUgZGF0YS4KClRoaXMgYmVpbmcgdGhlIGNhc2Ugd2Ugc2hvdWxkIHByb2NlZWQgd2l0aCBtb2RlbGxpbmcgdGVjaG5pcXVlcyB0aGF0IGFyZSBiYXNlZCBvbiAqemVyby1pbmZsYXRlZCogcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uLiBTdWNoIGRpc3RyaWJ1dGlvbnMgYWxsb3cgZm9yIGZyZXF1ZW50IHplcm8tdmFsdWVkIG9ic2VydmF0aW9ucyBhbmQgb25lIGNvbW1vbiBvbmUgaXMgYSB6ZXJvLWluZmxhdGVkIFBvaXNzb24gKFpJUCkgbW9kZWwsIGFzIHdlbGwgYXMgdGhlIHplcm8taW5mbGF0ZWQgbmVnYXRpdmUgYmlub21pYWwgKFpJTkIpLgoKSW4gb3JkZXIgdG8gZGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgemVyb3Mgd2l0aGluIGEgZGF0YXNldCwgd2UgY2FuIHNpbXBseSBwbG90IGEgaGlzdG9ncmFtIG9mIG9jY3VyZW5jZSBkYXRhLiBPZnRlbiB3aXRoaW4gc3VydnllIGRhdGEgc3BlY2llcyBhYnNlbnNlIGlzIGltcGxpY2l0IGluIHRoYXQgb25seSBhIHNwZWNpZXMgcHJlc2VuY2UgaXMgcmVjb3JkZWQuIElmIHRoaXMgaXMgdGhlIGNhc2UgdGhlbiB0aGUgZmlyc3QgdGhpbmcgd2Ugc2hvdWxkIGRvIGlzIHJlY29yZCB0aGUgYWJzZW5jZSBleHBsaWNpdGx5LiBJbiB0aGlzIHdheSwgd2UgaGF2ZSBhIHRydWUgcmVjb3JkIG9mIHRoZSAqc2FtcGxpbmcgZWZmb3J0Ki4KCklmIHdlIGxvb2sgYXQgdGhlIHNwZWNpZXMgZGF0YSBmb3IgR2lyYWZmZSBmcm9tIHRoZSBOYWJvaXNobyBkYXRhIHNldDoKCmBgYHtyIGdpcmFmZmVfZGF0YSwgZmlnLmFsaWduPSdjZW50ZXInLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIE51bWJlciBvZiBkaXN0aW5jdCB0cmFuc2VjdHMgdW5kZXJ0YWtlbgpuYWJvaXNobyAlPiUgCiAgZGlzdGluY3QoRGF0ZSxTYW1wbGUuTGFiZWwpICU+JQogIGNvdW50KCkKCiMgTnVtYmVyIG9mIHRyYW5zZWN0cyB3aGVyZSBHaXJhZmZlIHdlcmUgcmVjb3JkZWQKbmFib2lzaG8gJT4lIAogIGZpbHRlcihzcGVjaWVzID09ICJHaXJhZmZlIikgJT4lIAogIGRpc3RpbmN0KERhdGUsIFNhbXBsZS5MYWJlbCkgJT4lIAogIGNvdW50KCkKCiMgUGxvdCBhIGhpc3RvZ3JhbWUgb2Ygb2JzZXJ2YXRpb24gc2l6ZXMKbmFib2lzaG8gJT4lIAogIGZpbHRlcihzcGVjaWVzID09ICJHaXJhZmZlIiwgc2l6ZSA8IDMwMCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc2l6ZSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAiYmxhY2siLCBiaW53aWR0aCA9IDEpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKHggPSAiR2lyYWZmZSBncm91cCBzaXplIiwgCiAgICAgICAgIHkgPSAiRnJlcXVlbmN5IikKYGBgCgpTbyB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgd2VyZSAxMDk5IHRyYW5zZWN0cyB1bmRlcnRha2VuLCBidXQgb25seSAzNjEgb2YgdGhlbSBoYXZlIHJlY29yZHMgb2YgR2lyYWZmZS4gU28gd2UgaGF2ZSBub3QgZXhwbGljaXRseSByZWNvcmRlZCB0aGUgZmFjdCB0aGF0IGR1cmluZyA3MzggdHJhbnNlY3RzIG5vIEdpcmFmZmUgd2VyZSBvYnNlcnZlZC4gVGhlIGhpc3RvZ3JhbSBzaG93cyB0aGF0IHRoZSB2YXN0IG1ham9yaXR5IG9mIG9ic2VydmF0aW9ucyB3ZXJlIGp1c3QgZm9yIG9uZSBHaXJhZmZlLiBTbyBsZXQncyB1cGRhdGUgdGhlIGRhdGEgdG8gaW5jb3Jwb3JhdGUgdGhpczoKCmBgYHtyIGdpcmFmZmVfemVyb19pbmZsYXRlZCwgZmlnLmFsaWduPSdjZW50ZXInLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIE51bWJlciBvZiB0cmFuc2VjdHMgd2hlcmUgR2lyYWZmZSB3ZXJlIHJlY29yZGVkCmdpcmFmZmVfdHJhbnNlY3RzIDwtIG5hYm9pc2hvICU+JSAKICBmaWx0ZXIoc3BlY2llcyA9PSAiR2lyYWZmZSIpICU+JSAKICBkaXN0aW5jdChTYW1wbGUuTGFiZWwsIERhdGUpCiAgCiMgU2VsZWN0IHRyYW5zZWN0cyB3aGVyZSBubyB0YXJnZXQgc3BlY2llcyB3YXMgZGV0ZWN0ZWQKYWJzZW5jZV90cmFuc2VjdHMgPC0gbmFib2lzaG8gJT4lIAogIGRpc3RpbmN0KFNhbXBsZS5MYWJlbCwgRGF0ZSkgJT4lCiAgYW50aV9qb2luKC4sIGdpcmFmZmVfdHJhbnNlY3RzLCBjb3B5ID0gVCkgJT4lCiAgc2VtaV9qb2luKG5hYm9pc2hvLCAuLCBieT1jKCJTYW1wbGUuTGFiZWwiLCJEYXRlIiksIGNvcHk9VCkgJT4lIAogIGdyb3VwX2J5KFNhbXBsZS5MYWJlbCwgRGF0ZSkgJT4lIAogIHNsaWNlKDEpICU+JQogIG11dGF0ZShTcGVjaWVzID0gIk5BIiwKICAgICAgICAgZGlzdGFuY2UgPSBOQSwKICAgICAgICAgdmlzaXQgPSBOQSwKICAgICAgICAgc2l6ZSAgICAgPSAwKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkKCiMgQ29tYmluZSBtaXNzaW5nIHRyYW5zZWN0cyB3aXRoIHRhcmdldCBzcGVjaWVzIHRyYW5zZWN0cwpjb21wbGV0ZV90cmFuc2VjdHMgPC0gbmFib2lzaG8gJT4lCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkdpcmFmZmUiLCBzaXplIDwzMDApICU+JSAKICByYmluZChhYnNlbmNlX3RyYW5zZWN0c1ssMToxN10pCgojIFBsb3QgYSBoaXN0b2dyYW0gb2Ygb2JzZXJ2YXRpb24gc2l6ZXMgd2l0aCBhYnNlbmNlIHRyYW5zZWN0cyBpbmNsdWRlZApjb21wbGV0ZV90cmFuc2VjdHMgJT4lIAogIGdncGxvdChhZXMoeCA9IHNpemUpKSArIAogIGdlb21faGlzdG9ncmFtKGNvbG9yID0gImJsYWNrIiwgYmlud2lkdGggPSAxKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgbGFicyh4ID0gIkdpcmFmZmUgZ3JvdXAgc2l6ZSIsIAogICAgICAgICB5ID0gIkZyZXF1ZW5jeSIpCgojIENoZWNrIHRoYXQgdGhlIG51bWJlciBvZiB0cmFuc2VjdHMgYXJlIHdoYXQgd2UnZCBleHBlY3QgLSAxMDk5CmNvbXBsZXRlX3RyYW5zZWN0cyAlPiUgCiAgZGlzdGluY3QoRGF0ZSwgU2FtcGxlLkxhYmVsKSAlPiUgCiAgY291bnQoKQpgYGAKCk5vdyB3ZSBzZWUgdGhhdCBieSBpbmNsdWRpbmcgdGhlIHRyYW5zZWN0cyB3aGVyZSBHaXJhZmZlIHdlcmUgbm90IG9ic2VydmVkICh6ZXJvLWluZmxhdGluZyB0aGUgc3VydmV5IGRhdGEpIHRoZSBoaXN0b2dyYW0gaGFzIGNoYW5nZWQgc2lnbmlmaWNhbnRseS4KCiMjIEltcGxpY2F0aW9ucwpodHRwczovL2Z1a2FtaWxhYi5naXRodWIuaW8vQklPMjAyLzA0LUMtemVyby1kYXRhLmh0bWwKCiMgQ29sbGluZWFyaXR5CgpDb2xsaW5lYXJpdHkgaXMgd2hlbiBjb3ZhcmlhdGVzICh0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIG9mIGEgZGF0YSBzZXQpIGFyZSBzaWduaWZpY2FudGx5IGNvcnJlbGF0ZWQuICBUaGlzIGlzIHR5cGljYWxseSBhIHByb2JsZW0gYXMgY29sbGluZWFyaXR5IHdpbGwgaW5mbGF0ZSB0aGUgdmFyaWFuY2Ugb2YgbW9kZWxsZWQgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYW5kIHRoZXJlZm9yZSBpdCBpcyBpbXBvcnRhbnQgdG8gZGV0ZWN0IGFuZCByZW1vdmUgdGhlIHJlZHVuZGFuY3kgaW50cm9kdWNlZCwgYnkgZHJvcHBpbmcgb25lIG9mIHRoZSBjb3ZhcmlhdGVzIHdoZXJlIHRoZSBjb2xsaW5lYXJpdHkgaXMgb2JzZXJ2ZWQuIEZhaWx1cmUgdG8gZG8gdGhpcyB3aWxsIGluY3JlYXNlIHN0YW5kYXJkIGVycm9ycyBvZiByZWdyZXNzaW9uIHBhcmFtZXRlcnMgYW5kIHRoZXJlZm9yZSBpbmZsYXRlIHAtdmFsdWVzIFsxXl0gd2hlbiBpbmNvcnBvcmF0aW5nIHN1Y2ggZGF0YSBpbnRvIHJlZ3Jlc3Npb24gbW9kZWxzLiAKCkluIG9yZGVyIHRvIGRlbW9uc3RyYXRlIGNvbGxpbmVhcml0eSB3aXRoaW4gaW5kZXBlbmRlbnQgdmFyaWFibGVzLCB3ZSBjYW4gbG9vayBhdCB0aGUgYGRvdWJzYCBkYXRhc2V0W14yXS4gVGhpcyBpcyBhIGRhdGFzZXQgdGhhdCByZWNvcmRzIGZpc2ggc3BlY2llcyBmcm9tIGEgc3VydmV5IG9mIHRoZSByaXZlciBEb3VicyBpbiBGcmFuY2UsIHRvZ2V0aGVyIHdpdGggdGhlIHNwYXRpYWwgY29vcmRpbmF0ZXMgZm9yIHRoZSAzMCBzdXJ2ZXkgc2l0ZXMgYW5kIGVudmlyb25tZW50YWwgY292YXJpYXRlcyBmb3IgZWFjaCBvZiB0aGUgc2l0ZS4gV2UgY2FuIGV4YW1pbmUgY29sbGluZWFyaXR5IGJldHdlZW4gdGhlIGVudmlyb25tZW50YWwgY292YXJpdGVzIGZyb20gYGRvdWJzYCwgYnkgdXNpbmcgYSBjb3JyZWxhdGlvbiBtYXRyaXguIFRoaXMgc2hvd3Mgc2NhdHRlcnBsb3RzIGJldHdlZW4gY292YXJpdGUgZGF0YSwgdG9nZXRoZXIgd2l0aCBQZWFyc29uIGNvZWZmaWNpZW50cy4KCmBgYHtyIGVudl9jb3JyZWxhdGlvbiwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGggPSAxMCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShQZXJmb3JtYW5jZUFuYWx5dGljcykgCmxpYnJhcnkoYWRlNCkKCiNMZXQncyBsb2FkIHRoZSBkYXRhCmRhdGEoZG91YnMpCiMgRGF0YSByZXByZXNlbnRpbmcgd2F0ZXIgY2hlbWlzdHJ5IGFzc2F5IGZvciBlYWNoIHNpdGUuLi4KZW52IDwtIGRvdWJzJGVudgoKY2hhcnQuQ29ycmVsYXRpb24oZW52LCAKICAgICAgICAgICAgICAgICAgaGlzdG9ncmFtPVRSVUUsIAogICAgICAgICAgICAgICAgICBtZXRob2QgPSAicGVhcnNvbiIsCiAgICAgICAgICAgICAgICAgIHBjaD0xOSwKICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiQ29ycmVsYXRpb24gTWF0cml4IikKYGBgCgpXZSBjYW4gZWFzaWx5IHNlZSB0aGF0IHRoZXJlIGFyZSBhIG51bWJlciBvZiBjb3ZhcmlhdGVzIHdpdGggc2lnbmlmaWNhbnQgKD4gYy45MCUpIGNvbGxpbmVhcml0eSBiZXR3ZWVuLiBUaGUgZWNvbG9naWNhbCByZWFzb25zIGZvciB0aGlzIGNvcnJlbGF0aW9uIGFyZSBvdXRsaW5lZCBiZWxvdy4KClRvcG9sb2dpY2FsIGVudmlyb25tZW50YWwgY292YXJpYXRlczoKCiogYGRmc2AgKGRpc3RhbmNlIGZyb20gc291cmNlKSBhbmQgYGFsdGAgKGFsdGl0dWRlKSAtIEFzIHdlIG1vdmUgYXdheSBmcm9tIHRoZSBzb3VyY2UgYXQgdGhlb3Agb2YgYSBtb3VudGFpbiwgdGhlIGFsdGl0dWRlIGRlY3JlYXNlcy4KKiBgZGZzYCAoZGlzdGFuY2UgZnJvbSBzb3VyY2UpIGFuZCBgZmxvYCAobWVhbiBmbG93IHJhdGUpIC0gYWdhaW4sIGFzIHdlIG1vdmUgYXdheSBmcm9tIHRoZSBzb3VyY2UsIHRoZSBmbG93IHJhdGUgb2Ygd2F0ZXIgaW5jcmVhc2VzLgoqIGBhbHRgIChhbHRpdHVkZSkgYW5kIGBmbG9gIChtZWFuIGZsb3cgcmF0ZSkgLSBhcyB0aGUgYWx0aXR1ZGUgZGVjcmVhc2VzLCBhZ2FpbiB0aGUgZmxvdyByYXRlIGluY3JlYXNlcy4KCldhdGVyLWNoZW1pc3RyeSBlbnZpcm9ubWVudGFsIGNvdmFyaWF0ZXM6CgoqIGBwaG9gIChwaG9zcGhhdGUgY29uY2VudHJhdGUpIGFuZCBgYW1tYCAoYW1tb25pdW0gY29uY2VudHJhdGUpIC0gYW1tb25pdW0gaXMgYW4gaW5kaWNhdG9yIG9mIG9yZ2FuaWMgcG9sbHV0aW9uIGFuZCBwaG9zcGhvcnVzIGlzIGEgY29tbW9uIGNvbnN0aXR1ZW50IG9mIGFncmljdWx0dXJhbCBmZXJ0aWxpemVycywgbWFudXJlLCBhbmQgb3JnYW5pYyB3YXN0ZXMgW14zXS4gU28gaXQgd291bGQgYXBwZWFyIHRoYXQgbGlrZWx5IHRoYXQgdGhlaXIgam9pbiBwcmVzZW5jZSBpcyBkdWUgdG8gYWdyaWN1bHR1cmFsIGFjdGl2aXRpZXMuCiogYGFtbWAgKGFtbW9uaXVtIGNvbmNlbnRyYXRlKSBhbmQgYGJkb2AgKGJpb2xvZ2ljYWwgb3h5Z2VuIGRlbWFuZCkgLSBhbW1vbml1bSBhbmQgYmlvbG9naWNhbCBveHlnZW4gZGVtYW5kIGFyZSBrZXkgaW5kaWNhdG9ycyBvZiBvcmdhbmljIHBvbGx1dGlvbiBpbiB3YXRlci4gQk9EIHNob3dzIGhvdyBtdWNoIGRpc3NvbHZlZCBveHlnZW4gaXMgbmVlZGVkIGZvciB0aGUgZGVjb21wb3NpdGlvbiBvZiBvcmdhbmljIG1hdHRlciBwcmVzZW50IGluIHdhdGVyLgoKKiBgcGhvYCAocGhvc3BoYXRlIGNvbmNlbnRyYXRlKSBhbmQgYGJkb2AgKGJpb2xvZ2ljYWwgb3h5Z2VuIGRlbWFuZCkgLSBoaWdoIGxldmVscyBvZiBwaG9zcGhvcm91cyBpbiB3YXRlciBib2RpZXMgbGVhZCB0byBldXRyaXBoaWNhdGlvbiwgd2hpY2ggaXMgbGlrZWx5IHRvIGJlIGEgZGlyZWN0IHJlc3VsdCBvZiBwaG9zcGhhdGUgaW5wdXRzIGFuZCBhbW1vbml1bSBvcmdhbmljIHdhc3RlIGZyb20gZmFybWluZyBbXjNdLgoKRm9yIGVhY2ggb2YgdGhlIGNvdmFyaWF0ZXMgcGFpcnMgb3V0bGluZWQgYWJvdmUsIHRoZXJlIGlzIGNsZWFybHkgYSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlbS4gSW4gb3JkZXIgdG8gYXZvaWQgdGhlIHNpZGUtZWZmZWN0cyBtZW50aW9uZWQgYWJvdmUsIHdlIG5lZWQgdG8gc2VsZWN0IHRoZSBzbWFsbGVzdCBzdWJzZXQgb2YgY292YXJpYXRlcyB0aGF0IGV4cGxhaW5zIGFzIG11Y2ggb2YgdGhlIG92ZXJhbGwgdmFyaWF0aW9uIGluIHRoZSByZXNwb25zZSB2YXJpYWJsZShzKS4gV2Ugd2lsbCBiZSBleHBsb3JpbmcgaW5kcGVuZGVudCB2YXJpYWJsZSBzZWxlY3Rpb24gd2hlbiBmaXR0aW5nIG1vZGVscyBmb3Igc3BlY2llcyBkaXN0cmlidXRpb24gbW9kZWxsaW5nLgoKIyBBc3NvY2lhdGlvbnMKClZpc3VhbGlzaW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcmVzcG9uc2UgdmFyaWFibGUocykgYW5kIGNvdmFyaWF0ZXMgY2FuIGhlbHAgdG8gcHJvdmlkZSBhbiBpbml0aWFsIHVuZGVyc3RhbmRpbmcgdGhhdCBtYXkgYWlkIG1vZGVsIHNlbGVjdGlvbiB0ZWNobmlxdWUsIGFzIHdlbGwgYXMgaGlnaGxpZ2h0aW5nIG9ic2VydmF0aW9ucyB0aGF0IGRvIG5vdCBjb21wbHkgd2l0aCB0aGUgZ2VuZXJhbCBwYXR0ZXJuIGJldHdlZW4gcmVzcG9uc2UgYW5kIGNvdmFyaWF0ZSB2YXJpYWJsZXMuCgpXZSBjYW4gZG8gdGhpcyBmb3IgdGhlIGBkb3Vic2AgZGF0YSBzZXQuIFRoZSBwbG90IGJlbG93IHNob3dzIGZvciBlYWNoIGNvdmFyaWF0ZSAoeCBheGlzKSwgaG93IHRoZSBhYnVuZGFuY2UgZm9yIGRpZmZlcmVudCBzcGVjaWVzIHJlc3BvbmRzICh5IGF4aXMpLgoKYGBge3IgM2RzY2F0dGVycGxvdCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTEwLGZpZy5hc3A9Mn0KbGlicmFyeShyZXNoYXBlMikKCiMgTWVsdCBkYXRhZnJhbWUgdG8gZ2l2ZSBzcGVjaWVzIGRhdGEKc3BlY2llcyA8LSBkb3VicyRmaXNoCnNwZWNpZXMkc2l0ZSA8LSAxOjMwCmRmX21lbHRfc3BlY2llcyA8LSBtZWx0KHNwZWNpZXMsIAogICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlLnZhcnMgPSAxOjI4LCAKICAgICAgICAgICAgICAgICAgICAgICAgaWQudmFycyA9ICJzaXRlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlLm5hbWUgPSAiYWJ1bmRhbmNlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAic3BlY2llcyIpCiAgCmVudmlyb24gPC0gZG91YnMkZW52CmVudmlyb24kc2l0ZSA8LSAxOjMwCgojIE1lbHQgZGF0YWZyYW1lIHRvIGdpdmUgZW52aXJvbm1lbnRhbCBkYXRhCmRmX21lbHRfZW52aXJvbiA8LSBtZWx0KGVudmlyb24sIAogICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlLnZhcnMgPSAxOjExLCAKICAgICAgICAgICAgICAgICAgICAgICAgaWQudmFycyA9ICJzaXRlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlLm5hbWUgPSAiY292X3ZhbHVlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAiZW52X2NvdiIpCgojIE5vdyBwbG90IGpvaW5lZCBkYXRhIHRvIHNlZSByZXNwb25zZSAoc3BlY2llcyBhYnVuZGFuY2UpIHYgY292YXJpYXRlcwojIEJlZ2luIGJ5IGpvaW5pbmcgYm90aCBtZWx0ZWQgZGF0YXNldHMKZnVsbF9qb2luKGRmX21lbHRfc3BlY2llcyxkZl9tZWx0X2Vudmlyb24sInNpdGUiLCBrZWVwID0gRikgJT4lCiAgIyBSZW1vdmUgc2l0ZSBhcyBhIHNwZWNpZXMgY29sdW1uCiAgZmlsdGVyKHNwZWNpZXMgIT0gInNpdGUiKSAlPiUKICAKICBnZ3Bsb3QoKSArCiAgIyBQbG90IGFidW5kYW5jZSBmb3IgYWdhaW5zdCBlYWNoIGNvdmFyaWF0ZQogIGdlb21fcG9pbnQoYWVzKCB4ID0gY292X3ZhbHVlLCB5ID0gYWJ1bmRhbmNlKSwgc2l6ZSA9IDAuNSkgKwogICMgRml0IGEgR0FNIGZvciBlYWNoIHBsb3QgYW5kIHBsb3QgYSBzbW9vdGgKICBzdGF0X3Ntb290aChhZXMoIHggPSBjb3ZfdmFsdWUsIAogICAgICAgICAgICAgICAgICAgeSA9IGFidW5kYW5jZSksIAogICAgICAgICAgICAgIG1ldGhvZCA9ICJnYW0iLCAKICAgICAgICAgICAgICAjIFVzZSBjdWJpYyBzcGxpbmVzIGFzIGJhc2lzIGZvciBHQU0KICAgICAgICAgICAgICBmb3JtdWxhID0geSB+IHMoeCwgYnMgPSAiY3MiKSwgCiAgICAgICAgICAgICAgc2l6ZSA9IDAuNSkgKwogICMgUGxvdCBjb3ZhcmlhdGUgYnkgc3BlY2llcyAtIGJpZyBncmlkIQogIGZhY2V0X2dyaWQoc3BlY2llcyB+IGVudl9jb3YgLCBzY2FsZXM9ImZyZWUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICAjIEZsaXAgeCBheGlzIGxhYmVscyAtIG90aGVyd2lzZSB0aGV5IGRvbnQgZml0CiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKYGBgCgpXZSBjYW4gc2VlIHRoYXQgYWx0aG91Z2ggdGhlcmUgaXMgYSBsb3Qgb2YgaW5mb3JtYXRpb24gaGVyZSwgd2UgY2FuIHNlZSBzb21lIGNsZWFyIHR5cGVzIG9mIGJlaGF2aW91ciBmb3IgYWJ1bmRuYWNlIH4gY292YXJpYXRlLiBGb3IgZXhhbXBsZSwgc3BlY2llcyBhYnVuZGFuY2UgdHlwaWNhbGx5IHJlZHVjZXMgYXMgYWx0aXR1ZGUgaW5jcmVhc2VzLCBmb3IgYWxsIHNwZWNpZXMgdHlwZXMuIElmIHdlIGxvb2sgYXQgYGRmc2AgKGRpc3RhbmNlIGZyb20gc291cmNlKSwgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGFyZSBzb21lIHNwZWNpZXMgdGhhdCBoYXZlIHBlYWsgYWJ1bmRuYWNlIG1pZHN0cmVhbSAoQ29nbyBhbmQgU2F0ciksIHdoZXJlIGFzIG1vc3Qgb3RoZXJlIHNwZWNpZXMgaGF2ZSBtYXhpbXVtIGFidW5kYW5jZSBkb3duc3RyZWFtLCBhdCB0aGUgbW91dGggb2YgdGhlIHJpdmVyLiBXZSBjYW4gc2VlIHRoYXQgc3BlY2llcyBhYnVuZGFuY2UgaXMgcmVhbHRpdmVseSBoaWdoIGF0IGxvdyBsZXZlbHMgb2YgYW1tb25pbXVtIGZvciBhbGwgc3BlY2llcywgYnV0IGFidW5hbmNlIGlzIHplcm8gZm9yIGFueXRoaW5nIGFib3ZlIDUwIG1nTCReLTEkCgpXaHkgaXNuJ3QgcEggcGxvdHRpbmc/IQoKIyBSZWZlcmVuY2VzClteMV06IFp1dXIgQUYsIExlbm8gRU4sIEVscGhpY2sgQ1MgKDIwMTApIEEgcHJvdG9jb2wgZm9yIGRhdGEgZXhwbG9yYXRpb24gdG8gYXZvaWQgY29tbW9uIHN0YXRpc3RpY2FsIHByb2JsZW1zLiAqTWV0aG9kcyBpbiBFY29sb2d5IGFuZCBFdm9sdXRpb24qCgpbXjJdOiBWZXJuZWF1eCwgSi4gKDE5NzMpIENvdXJzIGQnZWF1IGRlIEZyYW5jaGUtQ29tdMOpIChNYXNzaWYgZHUgSnVyYSkuIFJlY2hlcmNoZXMgw6ljb2xvZ2lxdWVzIHN1ciBsZSByw6lzZWF1IGh5ZHJvZ3JhcGhpcXVlIGR1IERvdWJzLiBFc3NhaSBkZSBiaW90eXBvbG9naWUuIFRow6hzZSBkJ8OpdGF0LCBCZXNhbsOnb24uIDHigJMyNTcuICpEb3VicyByaXZlciBmaXNoIGNvbW11bml0aWVzKi4gaHR0cHM6Ly93d3cuZGF2aWR6ZWxlbnkubmV0L2FuYWRhdC1yL2Rva3UucGhwL2VuOmRhdGE6ZG91YnMKClteM106IGh0dHBzOi8vd3d3LmVlYS5ldXJvcGEuZXUvZGF0YS1hbmQtbWFwcy9pbmRpY2F0b3JzL2ZyZXNod2F0ZXItcXVhbGl0eS9mcmVzaHdhdGVyLXF1YWxpdHktYXNzZXNzbWVudC1wdWJsaXNoZWQtbWF5LTIKCg==